#ifndef __CONFIG_H__
#define __CONFIG_H__

#include <memory>
#include <string>
#include <sstream>
#include <boost/lexical_cast.hpp>
#include <yaml-cpp/yaml.h>
#include <algorithm>
#include <map>
#include <vector>
#include <list>
#include <set>
#include <unordered_set>
#include <unordered_map>
#include <functional>

#include "thread.h"
#include "sylar/log.h"


namespace sylar {


class ConfigVarBase
{
public:
    using ptr = std::shared_ptr<ConfigVarBase>;

    ConfigVarBase(const std::string& strName, const std::string& strDescription = "")
        : m_strName(strName), m_strDescription(strDescription)
    {
        std::transform(m_strName.begin(), m_strName.end(), m_strName.begin(), ::tolower);
    }

    virtual ~ConfigVarBase() { }

    const std::string& getName() const { return m_strName;  }
    const std::string& getDescription(  ) const { return m_strDescription; }

    virtual std::string toString() = 0;                     // 将yaml解析成string
    virtual bool fromString(const std::string& strYaml) = 0;    // 将string解析成yaml
    virtual std::string getTypeName() const = 0;
protected:
    std::string m_strName;          // 配置参数的名称
    std::string m_strDescription;   // 配置描述
};

// F:from type, T: to type
template<class F, class T>
class LexicalCast
{
public:
    T operator() (const F& val)
    {
        return boost::lexical_cast<T>(val);
    }
};

template<class T>
class LexicalCast<std::string, std::vector<T> >
{
public:
    std::vector<T> operator() (const std::string& val)
    {
        YAML::Node node = YAML::Load(val);
        typename std::vector<T> vec;
        std::stringstream ss;
        for(size_t i = 0; i < node.size(); ++i) 
        {
            ss.str("");
            ss << node[i];
            vec.push_back(LexicalCast<std::string, T>()(ss.str()));
        }
        return vec;
    }
};

template<class T>
class LexicalCast<std::vector<T>, std::string>
{
public:
    const std::string operator() (const std::vector<T>& val)
    {
        std::stringstream ss;
        YAML::Node node(YAML::NodeType::Sequence);
        for (auto& i : val)
        {
            node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
        }
        ss << node;
        return ss.str();
    }
};

template<class T>
class LexicalCast<std::string, std::list<T> >
{
public:
    std::list<T> operator() (const std::string& val)
    {
        YAML::Node node = YAML::Load(val);
        std::list<T> lst;
        std::stringstream ss;
        for(size_t i = 0; i < node.size(); ++i) 
        {
            ss.str("");
            ss << node[i];
            lst.push_back(LexicalCast<std::string, T>()(ss.str()));
        }
        return lst;
    }

};


template<class T>
class LexicalCast<std::list<T>, std::string>
{
public:
    const std::string operator() (const std::list<T>& val)
    {
        std::stringstream ss;
        YAML::Node node(YAML::NodeType::Sequence);
        for (auto& i : val)
        {
            node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
        }
        ss << node;
        return ss.str();
    }
};

template<class T>
class LexicalCast<std::string, std::set<T>>
{
public:
    std::set<T> operator() (const std::string& val)
    {
        YAML::Node node = YAML::Load(val);
        std::set<T> vec;
        std::stringstream ss;
        for (size_t i = 0; i < node.size(); i++)
        {
            ss.str("");
            ss << node[i];
            vec.insert(LexicalCast<std::string, T>()(ss.str()));
        }
        return vec;
    }
};

template<class T>
class LexicalCast <std::set<T>, std::string>
{
public:
    std::string operator() (const std::set<T>& val)
    {
        YAML::Node node(YAML::NodeType::Sequence);
        for (auto& i :val)
        {
            node.push_back(YAML::Load(LexicalCast<T,std::string>()(i)));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};

template<class T>
class LexicalCast<std::string, std::unordered_set<T>>
{
public:
    std::unordered_set<T> operator() (const std::string& val)
    {
        YAML::Node node = YAML::Load(val);
        std::unordered_set<T> vec;
        std::stringstream ss;
        for (size_t i = 0; i < node.size(); i++)
        {
            ss.str("");
            ss << node[i];
            vec.insert(LexicalCast<std::string, T>()(ss.str()));
        }
        return vec;
    }
};

template<class T>
class LexicalCast <std::unordered_set<T>, std::string>
{
public:
    std::string operator() (const std::unordered_set<T>& val)
    {
        YAML::Node node(YAML::NodeType::Sequence);
        for (auto& i :val)
        {
            node.push_back(YAML::Load(LexicalCast<T,std::string>()(i)));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};

//
template<class T>
class LexicalCast<std::string, std::map<std::string, T> >
{
public:
    std::map<std::string,T> operator() (const std::string& val)
    {
        YAML::Node node = YAML::Load(val);
        std::map<std::string,T> vec;
        std::stringstream ss;
        for (auto it = node.begin(); it  != node.end(); ++ it)
        {
            ss.str("");
            ss << it->second;
            vec.insert(std::make_pair(it->first.Scalar() ,LexicalCast<std::string, T>()(ss.str())));
        }
        return vec;
    }
};

template<class T>
class LexicalCast <std::map<std::string,T>, std::string>
{
public:
    std::string operator() (std::map<std::string,T>& val)
    {
        YAML::Node node(YAML::NodeType::Map);
        for (auto& i :val)
        {
            node[i.first] = YAML::Load(LexicalCast<T, std::string>()(i.second));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};


//
template<class T>
class LexicalCast<std::string, std::unordered_map<std::string,T>>
{
public:
   std::unordered_map<std::string,T> operator() (const std::string& val)
    {
        YAML::Node node = YAML::Load(val);
        std::unordered_map<std::string,T> vec;
        std::stringstream ss;
        for (auto it = node.begin(); it  != node.end(); ++ it)
        {
            ss.str("");
            ss << it->second;
            vec.insert(std::make_pair(it->first.Scalar() ,LexicalCast<std::string, T>()(ss.str())));
        }
        return vec;
    }
};

template<class T>
class LexicalCast <std::unordered_map<std::string,T>, std::string>
{
public:
    std::string operator() (std::unordered_map<std::string,T>& val)
    {
        YAML::Node node(YAML::NodeType::Map);
        for (auto& i :val)
        {
            node[i.first] = YAML::Load(LexicalCast<T, std::string>()(i.second));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};


// FromStr: T operator(const string&)
// ToStr: string operator(T)
template<class T, class FromStr = LexicalCast<std::string, T>
                , class ToStr   = LexicalCast<T, std::string> >
class ConfigVar : public ConfigVarBase
{
public:
    using RWMutexType = RWMutex;
    using ptr = std::shared_ptr<ConfigVar>;
    using on_change_cb = std::function<void (const T& old_val, const T& new_val)>;

    ConfigVar(const std::string& name
            ,const T& default_value
            ,const std::string& description = "")
        :ConfigVarBase(name, description)
        ,m_Val(default_value) 
    {

    }

    virtual std::string toString() override
    {   // 将yaml解析成string
        try
        {
            //return  boost::lexical_cast<std::string>(m_Val);
            RWMutexType::ReadLock lock(m_mutex);
            return ToStr()(m_Val);
        }
        catch(std::exception& e)
        {
            SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::toString ERROR: " << 
            e.what() << "covert to " << typeid(m_Val).name() << " to string";
        }
        return "";
    }                    

    virtual bool fromString(const std::string& strYaml) override
    {
        // 将string解析成yaml

        try
        {
            //m_Val = boost::lexical_cast<T>(strYaml);
            setValue(FromStr()(strYaml));
            return true;
        }
        catch(const std::exception& e)
        {
            SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::fromString ERROR: " << 
            e.what() << " from string conver to" << typeid(m_Val).name() << " - " << strYaml;
        }
        return false;
    }

    const T getValue()
    { 
        RWMutexType::ReadLock lock(m_mutex);
        return m_Val; 
    }

    void setValue(const T& val) 
    { 
        {
            RWMutexType::ReadLock lock(m_mutex);
            if (val == m_Val)
            {
                return;
            }
            for (auto i : m_cbs)
            {
                i.second(m_Val, val);
            }
        }
        RWMutexType::WriteLock lock(m_mutex);
        m_Val = val;
    }


    std::string getTypeName() const override { return typeid(T).name(); }

    void addListener(uint64_t key, on_change_cb cb)
    {
        RWMutexType::WriteLock lock(m_mutex); 
        m_cbs[key] = cb;
    }

    uint64_t addListener(on_change_cb cb)
    {
        static uint64_t s_fun_id = 0;
        RWMutexType::WriteLock lock(m_mutex); 
        m_cbs[s_fun_id++] = cb;
        return s_fun_id;
    }

    void delListener(uint64_t key)
    {
        RWMutexType::WriteLock lock(m_mutex);
        m_cbs.erase(key);
    }

    on_change_cb getListener(uint64_t key)
    {
        RWMutexType::ReadLock lock(m_mutex);
        auto it = m_cbs.find(key);
        return it == m_cbs.end() ? nullptr : it->second;
    }

    void clearListener()
    {
        RWMutexType::WriteLock lock(m_mutex);
        m_cbs.clear();
    }

private:
    T m_Val;

    RWMutexType m_mutex;
    // 变更回调 key:要求唯一 一般用hash
    std::map<uint64_t, on_change_cb> m_cbs;
};

class Config
{
public:
    using ConfigVarMap = std::map<std::string, ConfigVarBase::ptr>;
    using RWMutexType = RWMutex;
    // 查找 data中有时直接使用，没有插入
    template<class T>
    static typename ConfigVar<T>::ptr LookUp(const std::string& name, 
        const T& val, const std::string& description = "")
    {
        
        {
            RWMutexType::ReadLock lock(Config::GetMuetx());
            //auto tmp = LookUp<T>(name);
            auto it = GetDatas().find(name);
            if (it != GetDatas().end())
            {
                auto tmp = std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
                if (tmp)
                {
                    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "Lookup name=" << name << " exists";
                    return tmp;
                }
                else
                {
                    SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Lookup name=" << name << " exists but type not " << typeid(T).name() << "real_type= " << it->second->getTypeName() << " " << it->second->toString();

                    return nullptr;
                }
            }

        }
        RWMutexType::WriteLock lock(Config::GetMuetx());
        /// 只能用这个 abcdefghikjlmnopqrstuvwxyz._012345678
       if(name.find_first_not_of("abcdefghikjlmnopqrstuvwxyz._012345678") 
                != std::string::npos)
       {
           SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Look up " << name << "Invoild";
           throw std::invalid_argument(name);
       }
       typename ConfigVar<T>::ptr v  (new ConfigVar<T>(name, val, description));
       Config::GetDatas()[name] = v;
       return v;
    }

    /// @brief 查找 不插入
    /// @tparam T 
    /// @param name 
    /// @return 
    template<class T>
    static typename ConfigVar<T>::ptr LookUp(const std::string& name)
    {
        RWMutexType::ReadLock lock(Config::GetMuetx());
        auto it = Config::GetDatas().find(name);
        if (it == Config::GetDatas().end())
        {
            return nullptr;
        }
        return std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
    }

    static void LoadFromYaml(const YAML::Node& root);

    /// @brief 查找当前项
    /// @param name 
    /// @return 
    static ConfigVarBase::ptr LookUpBase(const std::string& name);

    static void Visit(std::function<void(ConfigVarBase::ptr)> cb);

private:
    // 声明需要定义
    // static ConfigVarMap s_datas; 旧版本初始化
    static ConfigVarMap& GetDatas()
    {   
        // 防止静态成员初始化顺序不一致问题导致core
        static ConfigVarMap s_datas;
        return s_datas;
    }

    static RWMutexType& GetMuetx()
    {
        static RWMutexType s_mutex;
        return s_mutex;
    }
};

}

#endif